/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef INCLUDE_VBS_TRANSPORTFACTORY_H_
#define INCLUDE_VBS_TRANSPORTFACTORY_H_

#include <string>
#include <map>
#include <ertps/utils/LocatorAlias.h>
#include <deps/common/LocatorsIterator.hpp>

#include <transport/MessageReceiverInterface.h>
#include <transport/ReceiverResource.h>
#include <transport/SenderResource.h>
#include <transport/TransportInterface.h>
#include <transport/TransportDescriptorInterface.h>

#include <vector>
#include <memory>
#include <utility>

using namespace vbsutil::xmlparser;

namespace vbs {
namespace transport {

enum class TransmitorType {
    TRANSMITOR_TYPE_UDP = 0,
    TRANSMITOR_TYPE_TCP,
    TRANSMITOR_TYPE_IPC,
#if !defined(_WIN32)
    TRANSMITOR_TYPE_UDS,
    TRANSMITOR_TYPE_UDS_STREAM,
#endif
    TRANSMITOR_TYPE_SHM,
    TRANSMITOR_TYPE_RAW,
    TRANSMITOR_TYPE_MAX
};

typedef int (*recieve_callback)(void* data, int32_t datalen, Locator_t& inputlocator, Locator_t& sourcelocator);
typedef int (*accept_callback)(Locator_t& inputlocator, Locator_t& sourcelocator);
using AsyncSendHandler = std::function<void(const asio::error_code&, size_t)>;

class TransportFactory;

class Transport {
    friend TransportFactory;

 public:
    TransportInterface* transmitor() { return transmitor_; }
    void transmitor(TransportInterface* transmitor) { transmitor_ = transmitor; }
    SendResourceList& sendResourceList() { return m_sendResourceList_; }
    std::map<Locator_t, std::shared_ptr<SenderResource>>& sendResourceMap() { return m_sendResourceMap_; }
    std::mutex& lockTransport() { return m_receiverResourceMutex_; }

    std::mutex& lockTransportSend() { return m_sendResourceMutex_; }
    void addReceiver(std::shared_ptr<ReceiverResource> rcv) {
        std::lock_guard<std::mutex> guard(m_receiverResourceMutex_);
        m_receiverResourceList_.push_back(rcv);
    }

    std::shared_ptr<SenderResource> getSenderResource(const Locator_t& locator) {
        std::lock_guard<std::mutex> guard(m_sendResourceMutex_);
        auto it = m_sendResourceMap_.find(locator);
        if (it != m_sendResourceMap_.end()) {
            return it->second;
        }

        return nullptr;
    }

 private:
    TransportInterface* transmitor_ = nullptr;

    std::list<std::shared_ptr<ReceiverResource>> m_receiverResourceList_;
    std::mutex m_receiverResourceMutex_;

    //! SenderResource List
    std::mutex m_sendResourceMutex_;
    SendResourceList m_sendResourceList_;
    std::map<Locator_t, std::shared_ptr<SenderResource>> m_sendResourceMap_;
};

class MessageReceiverInterface;
/**
 * Provides the library with abstract resources, which
 * in turn manage the SEND and RECEIVE operations over some transport.
 * Once a transport is registered, it becomes invisible to the library
 * and is abstracted away for good.
 */
class TransportFactory {
 public:
    TransportFactory();
    ~TransportFactory();

    static std::shared_ptr<TransportFactory> get_instance();

    /**
     * Allow registration of a transport dynamically.
     * @param name higelevel proto name
     * @param descriptor Structure that defines all initial configuration for a given transport.
     */
    bool RegisterTransport(std::string name, vbs::transport::TransportDescriptorInterface* descriptor);

    bool UnregisterTransport(std::string name, TransmitorType type = TransmitorType::TRANSMITOR_TYPE_MAX);

    bool bTransportRegistered(std::string name, TransmitorType type);

    /** opening every possible channel that can send through the given locator
     * @param name higelevel proto name
     * @param locator Locator through which to send.
     */
    bool BuildSendResources(std::string name, const Locator_t& locator);

    bool BuildSendResources(std::string, const Locator_t&, Locator_t&);

    /**
     * @param name higelevel proto name
     * @param local Locator from which to listen.
     * @param receiver_max_message_size Max message size allowed by the message receiver.
     */
    std::shared_ptr<ReceiverResource> BuildReceiverResources(std::string name, Locator_t& locator,
                                                             uint32_t receiver_max_message_size, bool& already_open,
                                                             MessageReceiverInterface* rcv = nullptr);

    /** send message synchronously
     * @param name highlevel proto name
     * @param data data to send.
     * @param dataLength length of data to send.
     * @param destination_locators_begin destination locator.
     * @param destination_locators_end destination locator.
     */
    bool Send(std::string name, unsigned char* data, uint32_t dataLength,
              const LocatorsIterator& destination_locators_begin, const LocatorsIterator& destination_locators_end);

    /** send message synchronously
     * @param name highlevel proto name
     * @param data data to send.
     * @param dataLength length of data to send.
     * @param destination_locator destination locator.
     */
    bool Send(std::string name, unsigned char* data, uint32_t dataLength, const Locator_t& destination_locator);

    /** send message asynchronously
     * @param name higelevel proto name
     * @param data data to send.
     * @param dataLength length of data to send.
     * @param destination_locators_begin destination locator.
     * @param destination_locators_end destination locator.
     */
    bool AsyncSend(std::string name, unsigned char* data, uint32_t dataLength,
                   const LocatorsIterator& destination_locators_begin, const LocatorsIterator& destination_locators_end,
                   AsyncSendHandler handler);

    bool AsyncSend(std::string name, unsigned char* data, uint32_t dataLength, const Locator_t& destination_locator,
                   AsyncSendHandler handler);

    bool CleanSendResource(std::string name, const Locator_t& remote_locator);
    void NormalizeLocators(std::string name, LocatorList& locators);
    bool transform_remote_locator(std::string name, const Locator_t& remote_locator, Locator_t& result_locator);

    bool is_local_locator(std::string name, const Locator_t& locator) const;
    bool is_locator_allowed(std::string name, const Locator_t& locator);
    bool hasRegisteredTransports(std::string name);
    void GetDefaultOutputLocators(std::string name, LocatorList& defaultLocators);
    bool getDefaultMetatrafficMulticastLocators(std::string name, LocatorList& locators,
                                                uint32_t metatraffic_multicast_port);
    bool fillMetatrafficMulticastLocator(std::string name, Locator_t& locator, uint32_t metatraffic_multicast_port);
    bool getDefaultMetatrafficUnicastLocators(std::string name, LocatorList& locators,
                                              uint32_t metatraffic_unicast_port);
    bool fillMetatrafficUnicastLocator(std::string name, Locator_t& locator, uint32_t metatraffic_unicast_port);
    bool getDefaultUnicastLocators(std::string name, LocatorList& locators, uint32_t unicast_port);
    bool fill_default_locator_port(std::string name, Locator_t& locator, uint32_t unicast_port);

    SendResourceList& getSendResource(std::string name, TransmitorType type);

    uint32_t GetMinSendBufferSize(std::string name, TransmitorType type);

    void Shutdown(std::string name);
    void update_network_interfaces(std::string name);
    bool select_multicast(std::string name, Locator_t& locator);
    bool DestroyReceiverResources(std::string name, Locator_t& local);

 private:
    // 单进程独享的单例对象必须禁止拷贝构造操作
    TransportFactory(const TransportFactory&) = delete;
    void operator=(const TransportFactory&) = delete;

    Transport* GetTransport(std::string name, TransmitorType type) const;
    TransmitorType LocatorTypeToTransportType(int32_t locator_kind) const;

    mutable std::mutex mtx_transport_;
    std::map<std::string, std::array<vbs::transport::Transport*,
                                     static_cast<long unsigned int>(TransmitorType::TRANSMITOR_TYPE_MAX)>>
        mRegisteredTransports;
};

}  // namespace transport
}  // namespace vbs

#endif  // INCLUDE_VBS_TRANSPORTFACTORY_H_
